/*
 * z80.hpp
 *
 *  Created on: Oct 31, 2011
 *      Author: dgdiniz
 */

/*
 * Documentation:
 *
 * 3 buses:
 * DataBus: 8bits bidirectional
 * Address Bus: 16bits unidirectional
 * Control Bus: 16bits
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

#ifndef Z80_HPP_
#define Z80_HPP_

#include <playlib/defines.hpp>

#define call(op) (this->*op)()

enum {_BC=0, _DE, _HL, _SP, _AF, _IX, _IY}; //used as index for register pairs
enum {_cNZ=0, _cZ, _cNC, _cC, _cPO, _cPE, _cP, _cM}; //condition ids
enum {_B=0,_C,_D,_E,_H,_L,_A,_F,_I,_R};


#include "z80_registers.hpp"

#include "z80_instructions.hpp"

class z80
{
protected:

	regs_t regs;


	uint8 displacement; //This is added to index registers in some instructions. This is where we put it.
	z80_instruction opcode;

	struct
	{
		FlipFlop IFF1;
		FlipFlop IFF2;
	}irq;


	void (z80::*op[0x10000])();

	void setFlags(u16 tmp_A);
	void opcodeTable();

///////////////////////// ADRESSING MODES///////////////////////////////////////////////////////////
	template <void (z80::*op_instruction)()> inline void implicitAddressing8();
	template <u8 indirectReg, void (z80::*op_instruction)()> inline void  implicitAddressing8_manual();
	template <u8 pairIdxReg, void (z80::*op_instruction)()> inline void  implicitAddressing16();
	template <void (z80::*op_instruction)()> inline void immediateAddressing();
	template <void (z80::*op_instruction)()> inline void immediateAddressing16();
	template <u8 indirectReg, void (z80::*op_instruction)()> inline void  indirectAddressingRead();
	template <u8 indirectReg, void (z80::*op_instruction)()> inline void  indirectAddressingWrite();
	template <u8 idxReg, void (z80::*op_instruction)()>	inline void  indexedAddressing();
	template <u8 idxReg, void (z80::*op_instruction)()>	inline void  indexedAddressingWrite();
	template <void (z80::*op_instruction)()> inline void relativeAddressing();
	template <void (z80::*op_instruction)()> inline void  directAddressing();
	template <void (z80::*op_instruction)()> inline void  directAddressing16();
	template <u8 externalReg, void (z80::*op_instruction)()> inline void  externalAddressing16();
	template <void (z80::*op_instruction)()> inline void  externalAddressing8();




///////////////////////// INSTRUCTION OPCODES///////////////////////////////////////////////////////
	inline void op_adc_8bits();
	inline void op_adc_16bits();
	inline void op_add_8bits();
	template <u8 pairIdxReg> inline void op_add_16bits();
	inline void op_and_8bits();
	inline void op_bit();
	template<bool hascondition> inline void op_call();
	inline void op_ccf();
	inline void op_cp();
	inline void op_cpd();
	inline void op_cpdr();
	inline void op_cpi();
	inline void op_cpir();
	inline void op_cpl();
	inline void op_daa();
	inline void op_dec_8bits();
	template<u8 regPair> inline void op_dec_16bits();
	inline void op_di();
	inline void op_djnz();
	inline void op_ei();
	template<bool hascondition> inline void op_jp();
	template<bool hascondition> inline void op_jr();
	template <bool fromMemory> inline void op_load_to_reg_auto();
	template <u8 IdxReg, bool fromMemory> inline void op_load_to_reg_manual();
	template <bool fromMemory> inline void op_load_to_pair_auto();
	template <u8 pairIdxReg, bool fromMemory> inline void op_load_to_pair_manual();
	inline void op_load_reg_to_mem_auto();
	template <u8 IdxReg> inline void op_load_reg_to_mem_manual();
	inline void op_load_pair_to_mem_auto();
	template <u8 pairIdxReg> inline void op_load_pair_to_mem_manual();
	inline void op_load_imm_to_mem();
	inline void op_ldd();
	inline void op_lddr();
	inline void op_ldi();
	inline void op_ldir();
	inline void op_neg();
	inline void op_nop();
	inline void op_or_8bits();
	inline void op_otdr();
	inline void op_otir();
	inline void op_out();
	inline void op_out_imm();
	inline void op_outd();
	inline void op_outi();
	inline void op_pop_auto();
	template <u8 pairIdxReg> inline void op_pop_manual();
	inline void op_push_auto();
	template <u8 pairIdxReg> inline void op_push_manual();
	inline void op_res_reg_auto();
	inline void op_res_mem();
	template<bool hascondition>  inline void op_ret();
	inline void op_reti();
	inline void op_retn();
	inline void op_rl();
	template <u8 pairIdxReg> inline void op_rl_mem();
	inline void op_rlc();
	template <u8 pairIdxReg> inline void op_rlc_mem();
	inline void op_rld();
	inline void op_rr();
	template <u8 pairIdxReg> inline void op_rr_mem();
	inline void op_rrc();


	inline void op_ret();
	inline void op_illegal();


	void z80_fetch();


	////////OTHERS//////////////
	inline u8 z80_condition(u8 condId);



	///////Test Variables
#ifdef Z80_TEST
	u8 OpTCycles;
	u8 OpMCycles;
#endif



public:
	z80()
	{
		displacement=0;
		opcodeTable();
	}

	void z80_reset();


	void z80_step();

	u8 readPC(int cycles);
	u8 fetchPC(int cycles);

	virtual u8 read8(u16 address, int cycles) = 0;
	virtual void write8(u16 address, u8 data, int cycles) = 0;
	virtual void sync(int cycles)=0;

	virtual void retCallback(int cycles) = 0;//only for test
};


#endif /* Z80_HPP_ */
